---------------------------------------------------------------------------------
-- Written by Switch Team: Billyde Brown, John Kang, Vincent Mao, David Wagner --
--------------------------------------------------------------------------------- 

-- Version history
---------------------------------------------------------------------------------
	--1.17, 5/4 2:00PM, Renamed inputs for coherence 

	--1.16, 5/3 9:10PM, Code tested on the board
-- Removed extraneous test code
-- Blackbox package made

	--1.15, 5/1 7:30PM, Code rewritten for coherency
-- Reordering of inputs and outputs
-- Rigorous testing of each state with waveforms generated

	--1.14, 4/30 12:50AM, Aging restored for testing (tested to be good earlier).
-- Memory problem tested out, modified code and recompiled full component

	--1.13, 4/24 6:00PM, LUT verified to be functional.  Aging commented out.

	--1.12, 4/24 3:35PM, Billy's code merged with John's code and Dave's block diagram
-- Overall waveform test file generated
-- Overall block diagram created
-- PMLUT debugging needed.

	--1.11, 4/24 4:39AM, Unsigned math worked out
--now enters OuterLoop correctly, but not inner loop
--problems are delays in memory response...see simulation
--gate delays as well?

	--1.1, 4/21 7:30PM, All code compiled and aging implemented
--explanation of aging:
--stamps are updated on write MAC, stamps are accessed on read MAC
--entries are expected to be stale
--if entries are >11 seconds apart, then will be stale
--only fresh, if entry was written to <11 seconds ago and accessed
--Status: compiles w/ 4 warnings, working on simulation
---------------------------------------------------------------------------------

-- I/O:
-- Inputs	: Port SRC address, MAC SRC address, MAC DST address
-- Outputs	: Port DST address, Broadcast (when Broadcast, Port DST is don't care)

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;


ENTITY PMLUT_age IS
	PORT(	Clock		:	IN	STD_LOGIC;
			RESETN		:	IN 	STD_LOGIC;	
			
			VLANReady	:	IN	STD_LOGIC;		
				
			Port_SRC	:	IN	STD_LOGIC_VECTOR (1 DOWNTO 0);
			MAC_SRC		:	IN	STD_LOGIC_VECTOR (47 DOWNTO 0);
			MAC_DST		:	IN	STD_LOGIC_VECTOR (47 DOWNTO 0);
		    PtrIN		: 	IN	STD_LOGIC_VECTOR (8 DOWNTO 0);			
			VlanTag		:	IN	STD_LOGIC_VECTOR (31 DOWNTO 0);
			TagValid	:	IN	STD_LOGIC;			

			FabricRdy	: 	IN  STD_LOGIC;
		
		 	LookUpRdy	: 	OUT STD_LOGIC := '1';
				
			Port_SRC_out:	OUT STD_LOGIC_VECTOR (1 DOWNTO 0);
			Port_DST	:	OUT	STD_LOGIC_VECTOR (1 DOWNTO 0);
			BCAST		:	OUT	STD_LOGIC;			
			
			PtrOUT		: 	OUT	STD_LOGIC_VECTOR (8 DOWNTO 0);
			VlanTagOUT	:	OUT	STD_LOGIC_VECTOR (31 DOWNTO 0);
			TagValidOUT	:	OUT	STD_LOGIC 
		); 
END PMLUT_age;	 


ARCHITECTURE behavior of PMLUT_age is
	TYPE State_type IS (WaitForReq, BusyStart, ReadData, WriteData, WaitState); 	
	SIGNAL State : State_type;	

	----------------------	
	-- Memory variables --
	----------------------
	
	SIGNAL	data_memBlock			: STD_LOGIC_VECTOR (63 DOWNTO 0);
	SIGNAL	rdaddress_memBlock		: STD_LOGIC_VECTOR (7 DOWNTO 0);
	SIGNAL	rd_en_memBlock			: STD_LOGIC  := '1';	
	SIGNAL	wraddress_memBlock		: STD_LOGIC_VECTOR (7 DOWNTO 0);
	SIGNAL	wr_en_memBlock			: STD_LOGIC  := '1';
	SIGNAL	data_out_memBlock		: STD_LOGIC_VECTOR (63 DOWNTO 0);
	
	-------------------------------------------
	-- Aging variables (assumes 50MHz clock) --
	-------------------------------------------
	
	SHARED VARIABLE BigCounter		: UNSIGNED (13 DOWNTO 0) := (OTHERS => '0'); 	-- BigCounter rolls over ~1.018 days.
	SHARED VARIABLE SmallCounter 	: UNSIGNED (27 DOWNTO 0) := (OTHERS => '0'); 	-- SmallCounter rolls over every ~5.369 seconds	
	SHARED VARIABLE TimeStamp		: STD_LOGIC_VECTOR (13 DOWNTO 0) := (OTHERS => '0');	
	CONSTANT FreshThresh	   		: UNSIGNED := "0010";   	-- For checking if entry is stale
	----------------------------------------------------------
	-- Threshold for freshness is realtime ~10.74 seconds,  --
	-- which corresponds to threshold = 2 					--
	-- For simulation purposes, assume it's 12 clock cycles	--
	-- Based on BigCounter counts. 							--
	----------------------------------------------------------
	
	----------------------								
	-- Shared variables --
	----------------------
	
	SHARED VARIABLE Mem_TimeStamp	: STD_LOGIC_VECTOR(13 DOWNTO 0) := (OTHERS => '0');
	SHARED VARIABLE Mem_MAC			: STD_LOGIC_VECTOR(47 DOWNTO 0) := (OTHERS => '0');
	
	SHARED VARIABLE Search_prt_dest	: STD_LOGIC_VECTOR(7 DOWNTO 0) := (OTHERS => '0');	-- For getting port addy from MAC addy for read
	SHARED VARIABLE Search_prt_src	: STD_LOGIC_VECTOR(7 DOWNTO 0) := (OTHERS => '0');	-- For getting port addy from MAC addy for write

-------------------------------------------
-- LUT memory block component definition --
-------------------------------------------
	
COMPONENT memBlock 
	PORT
	(
		clock		: IN STD_LOGIC ;
				
		data_in		: IN STD_LOGIC_VECTOR (63 DOWNTO 0); 		
		rdaddress	: IN STD_LOGIC_VECTOR (7 DOWNTO 0);
		rd_en		: IN STD_LOGIC  := '1';				 
		wraddress	: IN STD_LOGIC_VECTOR (7 DOWNTO 0);
		wr_en		: IN STD_LOGIC  := '1';				 
		
		data_out	: OUT STD_LOGIC_VECTOR (63 DOWNTO 0)
	);
END COMPONENT;

-----------------------
-- State definitions --
-----------------------	

BEGIN
	FSM_transitions	:  PROCESS (RESETN, CLOCK)

	BEGIN 	
		IF RESETN = '1' THEN			
			State <= WaitForReq;  				-- VLAN hasn't sent anything yet, remain in this state
			SmallCounter 	:= (OTHERS => '0');	-- Reset counters on RESET 
			BigCounter 		:= (OTHERS => '0');	 
			-- Windtunnel signals for the switch fabric to receive			
			PtrOUT			<= (OTHERS => '0');
			VlanTagOUT		<= (OTHERS => '0');
			TagValidOUT		<= '0';
			Port_SRC_out	<= (OTHERS => '0');
			LookUpRdy		<= '1';	
			BCAST			<= '1';
			
		ELSIF(CLOCK'EVENT AND CLOCK = '1')THEN 			
			SmallCounter := SmallCounter + 1;	-- Increment 28-bit counter
			
			IF (SmallCounter = 0) 				-- If 28-bit overflow, increment 14-bit counter
				THEN BigCounter := BigCounter + 1; 
			END IF;
			
			CASE State IS 
			
			WHEN WaitForReq =>      
				-- Waiting for the VLAN to send something
				wr_en_memBlock <= '0'; 
				rd_en_memBlock <= '0';	    
				-- Windtunnel signals for the switch fabric to receive			
				PtrOUT			<= (OTHERS => '0');
				VlanTagOUT		<= (OTHERS => '0');
				TagValidOUT		<= '0';
				Port_SRC_out	<= (OTHERS => '0');	
				BCAST			<= '1'; 
				IF VLANReady = '0' THEN
					State <= WaitForReq;
					LookUpRdy <= '1';
				ELSE 
					State <= BusyStart;  
					LookUpRdy <= '0';
				END IF;
								
			WHEN BusyStart =>
				-- LUT searches for the port destination from the MAC destination address			
				Search_prt_dest := MAC_DST(7 DOWNTO 0);
				rdaddress_memBlock <= Search_prt_dest;
				State <= ReadData;	
			
			WHEN ReadData =>	
				-- Toggle memory block component for reading LUT data
					rd_en_memBlock <= '1';			
				-- If fresh value, then read Port_DST
				-- Assume there's is no match present in LUT
					BCAST <= '1';			 
				
				-- If the age is < threshold, then broadcast
					IF (BigCounter-UNSIGNED(data_out_memBlock(63 DOWNTO 50)) < FreshThresh) THEN   
						IF data_out_memBlock(47 DOWNTO 0) = (MAC_DST) THEN
							Port_DST 	<= data_out_memBlock(49 DOWNTO 48);
							BCAST 		<= '0';	-- BCAST only off if entry is fresh AND found in LUT 
						END IF;
					END IF;	
					wr_en_memBlock 	<= '0';
				
				-- Windtunnel signals for the switch fabric to receive			
					PtrOUT			<= PtrIN;
					VlanTagOUT		<= VlanTag;
					TagValidOUT		<= TagValid;
					Port_SRC_out	<= Port_SRC;
					State 			<= WriteData;
		
				-- Convert MAC_SRC into an address and write Port_SRC to that address
				
					Search_prt_src := MAC_SRC(7 DOWNTO 0);
					wraddress_memBlock <= Search_prt_src;
		
				-- Concatenate data with the time stamp
					TimeStamp := CONV_STD_LOGIC_VECTOR(BigCounter, 14);
					data_memBlock <= (TimeStamp & Port_SRC & MAC_SRC);				
					
			WHEN WriteData=>
				-- Toggle memory block for writing
				rd_en_memBlock <= '0'; 
				wr_en_memBlock <= '1';

				-- Check if fabric is ready to receive outputs				
				IF FabricRdy = '1' THEN
					-- Toggle on write enable	
					State <= WaitForReq;
					LookUpRdy <= '1';
				ELSIF FabricRdy = '0' THEN
					State <= WaitState;
				END IF;
				
			WHEN WaitState=>
				IF FabricRdy = '1' THEN
					State <= WaitforReq;
					LookUpRdy <= '1';
				ELSE
					State <= WaitState;
					LookUpRdy <= '0';
				END IF;
			END CASE;
			
		END IF; 
	END PROCESS;
	
	
MemoryAccess:  memBlock
	PORT MAP (	clock, 
				data_memBlock, 
				rdaddress_memBlock, 
				rd_en_memBlock, 
				wraddress_memBlock, 
				wr_en_memBlock, 
				data_out_memBlock
			);
END Behavior; 	


LIBRARY ieee;
USE ieee.std_logic_1164.all;

LIBRARY altera_mf;
USE altera_mf.all;

ENTITY memBlock IS
	PORT(
			clock		: IN STD_LOGIC ;
			data_in		: IN STD_LOGIC_VECTOR (63 DOWNTO 0);
			rdaddress	: IN STD_LOGIC_VECTOR (7 DOWNTO 0);
			rd_en		: IN STD_LOGIC  := '1';	
			wraddress	: IN STD_LOGIC_VECTOR (7 DOWNTO 0);
			wr_en		: IN STD_LOGIC  := '1';
			data_out	: OUT STD_LOGIC_VECTOR (63 DOWNTO 0)
		);
END memBlock;


ARCHITECTURE SYN OF memBlock IS

	SIGNAL sub_wire0	: STD_LOGIC_VECTOR (63 DOWNTO 0);

	COMPONENT altsyncram
	GENERIC(
				address_reg_b						: STRING;
				clock_enable_input_a				: STRING;
				clock_enable_input_b				: STRING;
				clock_enable_output_a				: STRING;
				clock_enable_output_b				: STRING;
				intended_device_family				: STRING;
				lpm_type							: STRING;
				numwords_a							: NATURAL;
				numwords_b							: NATURAL;
				operation_mode						: STRING;
				outdata_aclr_b						: STRING;
				outdata_reg_b						: STRING;
				power_up_uninitialized				: STRING;
				ram_block_type						: STRING;
				rdcontrol_reg_b						: STRING;
				read_during_write_mode_mixed_ports	: STRING;
				widthad_a							: NATURAL;
				widthad_b							: NATURAL;
				width_a								: NATURAL;
				width_b								: NATURAL;
				width_byteena_a						: NATURAL
			);
	PORT(
			wren_a		: IN STD_LOGIC ;
			clock0		: IN STD_LOGIC ;
			address_a	: IN STD_LOGIC_VECTOR (7 DOWNTO 0);
			address_b	: IN STD_LOGIC_VECTOR (7 DOWNTO 0);
			rden_b		: IN STD_LOGIC ;
			q_b			: OUT STD_LOGIC_VECTOR (63 DOWNTO 0);
			data_a		: IN STD_LOGIC_VECTOR (63 DOWNTO 0)
		);
	END COMPONENT;


BEGIN
	data_out <= sub_wire0(63 DOWNTO 0);

	altsyncram_component : altsyncram
	GENERIC MAP(
					address_reg_b 						=> "CLOCK0",
					clock_enable_input_a 				=> "BYPASS",
					clock_enable_input_b 				=> "BYPASS",
					clock_enable_output_a				=> "BYPASS",
					clock_enable_output_b				=> "BYPASS",
					intended_device_family 				=> "Cyclone II",
					lpm_type 							=> "altsyncram",
					numwords_a 							=> 256,
					numwords_b 							=> 256,
					operation_mode 						=> "DUAL_PORT",
					outdata_aclr_b 						=> "NONE",
					outdata_reg_b 						=> "CLOCK0",
					power_up_uninitialized 				=> "FALSE",
					ram_block_type 						=> "M4K",
					rdcontrol_reg_b 					=> "CLOCK0",	
					read_during_write_mode_mixed_ports 	=> "DONT_CARE",
					widthad_a 							=> 8,
					widthad_b 							=> 8,
					width_a 							=> 64,
					width_b 							=> 64,
					width_byteena_a 					=> 1
				)
	
	PORT MAP(
				wren_a 		=> wr_en,
				clock0 		=> clock,
				address_a 	=> wraddress,
				address_b 	=> rdaddress,
				rden_b 		=> rd_en,	
				data_a 		=> data_in,
				q_b 		=> sub_wire0
			);

END SYN;
